﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Xml;

namespace Miao
{
    public static class DirectXmlCrossRefLoader
    {
        /// <summary>
        /// 缺乏的Ref
        /// </summary>
        private abstract class WantedRef
        {
            public object wanter;

            public abstract bool TryResolve(FailMode failReportMode);

            public virtual void Apply()
            {
            }
        }
        /// <summary>
        /// 所需Ref
        /// </summary>
        private class WantedRefForObject : WantedRef
        {
            public FieldInfo fieldInfo;

            public string defName;

            public Def resolvedDef;
            /// <summary>
            /// 所需Mod
            /// </summary>
            public string mayRequireMod;

            public Type overrideFieldType;

            private bool BadCrossRefAllowed
            {
                get
                {
                    if (!string.IsNullOrEmpty(mayRequireMod)/* && !ModsConfig.AreAllActive(mayRequireMod)*/)
                    {
                        return true;
                    }
                    return false;
                }
            }

            public WantedRefForObject(object wanter, FieldInfo fi, string targetDefName, string mayRequireMod = null, Type overrideFieldType = null)
            {
                base.wanter = wanter;
                this.fieldInfo = fi;
                defName = targetDefName;
                this.mayRequireMod = mayRequireMod;
                this.overrideFieldType = overrideFieldType;
            }

            //这一步根据类型和def去找指定DefDatabase中的数值
            public override bool TryResolve(FailMode failReportMode)
            {
                if (fieldInfo == null)
                {
                    Log.Error("Trying to resolve null field for def named " + defName);
                    return false;
                }
                Type type = overrideFieldType ?? fieldInfo.FieldType;
                //通过type和defName获取唯一的def
                resolvedDef = MiaoDefDatabase.GetDefSilentFail(type, defName);//这段回头改为委托，只进行一次反射开销
                if (resolvedDef == null)
                {
                    return false;
                }
                fieldInfo.SetValue(wanter, resolvedDef);
                return true;
            }
        }

        private class WantedRefForList<T> : WantedRef
        {
            private List<string> defNames = new List<string>();

            private List<string> mayRequireMods;

            private object debugWanterInfo;

            public WantedRefForList(object wanter, object debugWanterInfo)
            {
                base.wanter = wanter;
                this.debugWanterInfo = debugWanterInfo;
            }

            public void AddWantedListEntry(string newTargetDefName, string mayRequireMod = null)
            {
                if (!mayRequireMod.NullOrEmpty() && mayRequireMods == null)
                {
                    mayRequireMods = new List<string>();
                    for (int i = 0; i < defNames.Count; i++)
                    {
                        mayRequireMods.Add(null);
                    }
                }
                defNames.Add(newTargetDefName);
                if (mayRequireMods != null)
                {
                    mayRequireMods.Add(mayRequireMod);
                }
            }

            public override bool TryResolve(FailMode failReportMode)
            {
                bool flag = false;
                for (int i = 0; i < defNames.Count; i++)
                {
                    bool flag2 = mayRequireMods != null && i < mayRequireMods.Count && !mayRequireMods[i].NullOrEmpty() && !ModsConfig.AreAllActive(mayRequireMods[i]);
                    T val = TryResolveDef<T>(defNames[i], (!flag2) ? failReportMode : FailMode.Silent, debugWanterInfo);
                    if (val != null)
                    {
                        ((List<T>)wanter).Add(val);
                        defNames.RemoveAt(i);
                        if (mayRequireMods != null && i < mayRequireMods.Count)
                        {
                            mayRequireMods.RemoveAt(i);
                        }
                        i--;
                    }
                    else
                    {
                        flag = true;
                    }
                }
                return !flag;
            }
        }
        /// <summary>
        /// Dictionary延迟到Def交叉引用时再进行所需解析单位
        /// </summary>
        /// <typeparam name="K"></typeparam>
        /// <typeparam name="V"></typeparam>
        private class WantedRefForDictionary<K, V> : WantedRef
        {
            private List<XmlNode> wantedDictRefs = new List<XmlNode>();

            private object debugWanterInfo;

            private List<Tuple<object, object>> makingData = new List<Tuple<object, object>>();

            public WantedRefForDictionary(object wanter, object debugWanterInfo)
            {
                base.wanter = wanter;
                this.debugWanterInfo = debugWanterInfo;
            }

            public void AddWantedDictEntry(XmlNode entryNode)
            {
                wantedDictRefs.Add(entryNode);
            }

            public override bool TryResolve(FailMode failReportMode)
            {
                failReportMode = FailMode.LogErrors;
                bool flag = typeof(Def).IsAssignableFrom(typeof(K));
                bool flag2 = typeof(Def).IsAssignableFrom(typeof(V));
                foreach (XmlNode wantedDictRef in wantedDictRefs)
                {
                    XmlNode xmlNode = wantedDictRef["key"];
                    XmlNode xmlNode2 = wantedDictRef["value"];
                    object first = ((!flag) ? xmlNode : ((object)TryResolveDef<K>(xmlNode.InnerText, failReportMode, debugWanterInfo)));
                    object second = ((!flag2) ? xmlNode2 : ((object)TryResolveDef<V>(xmlNode2.InnerText, failReportMode, debugWanterInfo)));
                    makingData.Add(new Tuple<object, object>(first, second));
                }
                return true;
            }

            public override void Apply()
            {
                Dictionary<K, V> dictionary = (Dictionary<K, V>)wanter;
                dictionary.Clear();
                foreach (Tuple<object, object> makingDatum in makingData)
                {
                    try
                    {
                        object obj = makingDatum.Item1;
                        object obj2 = makingDatum.Item2;
                        if (obj is XmlNode)
                        {
                            obj = DirectXmlToObject.ObjectFromXml(obj as XmlNode, typeof(K));
                        }
                        if (obj2 is XmlNode)
                        {
                            obj2 = DirectXmlToObject.ObjectFromXml(obj2 as XmlNode, typeof(V));
                        }
                        dictionary.Add((K)obj, (V)obj2);
                    }
                    catch
                    {
                        Log.Error(string.Concat("Failed to load key/value pair: ", makingDatum.Item1, ", ", makingDatum.Item2));
                    }
                }
            }
        }

        private static List<WantedRef> wantedRefs = new List<WantedRef>();

        private static Dictionary<object, WantedRef> wantedListDictRefs = new Dictionary<object, WantedRef>();

        public static bool LoadingInProgress => wantedRefs.Count > 0;


        /// <summary>
        /// 
        /// </summary>
        /// <param name="wanter=需要添加的类"></param>
        /// <param name="field=字段"></param>
        /// <param name="targetDefName=目标Def"></param>
        /// <param name="mayRequireMod=目标引用"></param>
        /// <param name="assumeFieldType"></param>
        public static void RegisterObjectWantsCrossRef(object wanter, FieldInfo field, string targetDefName, string mayRequireMod = null, Type assumeFieldType = null)
        {

            WantedRefForObject item = new WantedRefForObject(wanter, field, targetDefName, mayRequireMod, assumeFieldType);
            wantedRefs.Add(item);
        }

        public static void RegisterObjectWantsCrossRef(object wanter, string fieldName, string targetDefName, string mayRequireMod = null, Type overrideFieldType = null)
        {
            WantedRefForObject item = new WantedRefForObject(wanter, wanter.GetType().GetField(fieldName), targetDefName, mayRequireMod, overrideFieldType);
            wantedRefs.Add(item);
        }

        public static void RegisterObjectWantsCrossRef(object wanter, string fieldName, XmlNode parentNode, string mayRequireMod = null, Type overrideFieldType = null)
        {
            string mayRequireMod2 = mayRequireMod ?? parentNode.Attributes?["MayRequire"]?.Value.ToLower();
            WantedRefForObject item = new WantedRefForObject(wanter, wanter.GetType().GetField(fieldName), parentNode.Name, mayRequireMod2, overrideFieldType);
            wantedRefs.Add(item);
        }

        public static void RegisterListWantsCrossRef<T>(List<T> wanterList, string targetDefName, object debugWanterInfo = null, string mayRequireMod = null)
        {

            WantedRefForList<T> wantedRefForList = null;
            if (!wantedListDictRefs.TryGetValue(wanterList, out var value))
            {
                wantedRefForList = new WantedRefForList<T>(wanterList, debugWanterInfo);
                wantedListDictRefs.Add(wanterList, wantedRefForList);
                wantedRefs.Add(wantedRefForList);
            }
            else
            {
                wantedRefForList = (WantedRefForList<T>)value;
            }
            wantedRefForList.AddWantedListEntry(targetDefName, mayRequireMod);

        }

        public static void RegisterDictionaryWantsCrossRef<K, V>(Dictionary<K, V> wanterDict, XmlNode entryNode, object debugWanterInfo = null)
        {

            WantedRefForDictionary<K, V> wantedRefForDictionary = null;
            if (!wantedListDictRefs.TryGetValue(wanterDict, out var value))
            {
                wantedRefForDictionary = new WantedRefForDictionary<K, V>(wanterDict, debugWanterInfo);
                wantedRefs.Add(wantedRefForDictionary);
                wantedListDictRefs.Add(wanterDict, wantedRefForDictionary);
            }
            else
            {
                wantedRefForDictionary = (WantedRefForDictionary<K, V>)value;
            }
            wantedRefForDictionary.AddWantedDictEntry(entryNode);

        }

        public static T TryResolveDef<T>(string defName, FailMode failReportMode, object debugWanterInfo = null)
        {

            T val = (T)(object)MiaoDefDatabase.GetDefSilentFail(typeof(T), defName);
            if (val != null)
            {
                return val;
            }
            if (failReportMode == FailMode.LogErrors)
            {
                string text = string.Concat("Could not resolve cross-reference to ", typeof(T), " named ", defName);
                if (debugWanterInfo != null)
                {
                    text = text + " (wanter=" + debugWanterInfo + ")";
                }
                Log.Error(text);
            }
            return default(T);

        }

        public static void Clear()
        {

            wantedRefs.Clear();
            wantedListDictRefs.Clear();
        }


        /// <summary>
        /// 并线执行所需解析
        /// </summary>
        /// <param name="failReportMode"></param>
        public static void ResolveAllWantedCrossReferencesParallelForEach(FailMode failReportMode)
        {
            HashSet<WantedRef> resolvedRefs = new HashSet<WantedRef>();
            object resolvedRefsLock = new object();
            MiaoThreading.ParallelForEach(wantedRefs, delegate (WantedRef wantedRef)
            {
                if (wantedRef.TryResolve(failReportMode))
                {
                    lock (resolvedRefsLock)
                    {
                        resolvedRefs.Add(wantedRef);
                    }
                }
            });
            foreach (WantedRef item in resolvedRefs)
            {
                item.Apply();
            }
            wantedRefs.RemoveAll((WantedRef x) => resolvedRefs.Contains(x));

        }
    }
}


